//你好
const util = String.raw/* cpp */`

  typedef struct {
    void* buffer;
    int width;
    int height;
    JSValue js_obj;
  } Image;

  static void js_image_finalizer(JSRuntime *rt, JSValue val) {
    printf("js_image_finalizer\n");
    Image *p = (Image*)JS_GetOpaque(val);
    if(p){
      free(p->buffer);
      free(p);
    }
  }

  static JSValue js_util_getRGB565(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    if(argc==1){
      int rgb;
      JS_ToInt32(ctx, &rgb, argv[0]);
      int ret = getRGB565(rgb);
      return JS_NewInt32(ctx, ret);
    }else if(argc==3){
      int r;
      JS_ToInt32(ctx, &r, argv[0]);
      int g;
      JS_ToInt32(ctx, &g, argv[1]);
      int b;
      JS_ToInt32(ctx, &b, argv[2]);

      int ret = getRGB565sep(r, g, b);
      return JS_NewInt32(ctx, ret);
    }
    return JS_UNDEFINED;
  }

  int read_int(FIL *fil) {
    char b[4];
    unsigned int br;
    f_read(fil, b, 4, &br);//fread(b, 1, 4, fp);
    return (int)b[0] | ((int)b[1] << 8) | ((int)b[2] << 16) | ((int)b[3] << 24);
  }

  short read_short(FIL *fil) {
    char b[2];
    unsigned int br;
    f_read(fil, b, 2, &br);//fread(b, 1, 2, fp);
    return (short)(b[0] | (b[1] << 8));
  }

  static void* decodeBMP(FIL *fil, int* w, int* h){

    unsigned int br;
    f_lseek(fil, 10);
    int pixel_offset = read_int(fil);

    f_lseek(fil, 18);
    int width = read_int(fil);
    int height = read_int(fil);

    f_lseek(fil, 28);
    short bpp = read_short(fil);

    if (bpp != 24) {
        f_close(fil);
        return 0;
    }

    int rowSize = ((width * 3 + 3) / 4) * 4;

    *w = width;
    *h = height;
    int byte_length = width * height * 2;
    short* p = (short*)malloc(byte_length);

    int padding = rowSize - width * 3;
    f_lseek(fil, pixel_offset);
    for (int y = height - 1; y >= 0; y--) {
      for (int x = 0; x < width; x++) {
        char bgr[3];
        f_read(fil, bgr, 3, &br);
        short val = getRGB565sep(bgr[2], bgr[1], bgr[0]);
        p[y * width + x] = val;
      }
      pixel_offset += width * 3 + padding;
      f_lseek(fil, pixel_offset);
    }
    refreshInsCache();
    flushDataCache();
    return p;

  }

  static FIL *file;
  size_t loader(JDEC *jdec, uint8_t* buf, size_t len){
    FIL *fil = (FIL*)(jdec->p);
      unsigned int br;
    if(buf){
      f_read(fil, buf, len, &br);
    }else{
      br = len;
      f_lseek(fil, fil->fptr+len);
    }
    return br;
  }

  #define DECODE_BUFFER_LEN 4096
  static void* decodeJPG(FIL *fil, int* w, int* h){
//void drawjpg(void* in, int len, void* imgBuff){
    char decodeBuffer[DECODE_BUFFER_LEN];
    JDEC jdec;
    //jdec.p = fil;
    //file = fil;
    JRESULT res;
    //printf("start\r\n");
    res = jd_prepare(&jdec, loader, (void*)decodeBuffer, (size_t)DECODE_BUFFER_LEN, fil);
    //printf("prep ok\r\n");
    //int jpgw = jdec.width;
    //int jpgh = jdec.height;

    int width = align(jdec.width, 4);
    int height = jdec.height;
    if(width>1024){
      width=1024;
    }
    if(height>768){
      height=768;
    }

    *w = width;
    *h = height;
    int byte_length = width * height * 2;
    short* p = (short*)malloc(byte_length);
    //short* p = (short*)malloca(byte_length, 1024);

    jdec.imgBuff = p;
    
    jd_decomp(&jdec, (int (*)(JDEC*, void*, JRECT*))NULL, 0, 0, 0, width, height);

    refreshInsCache();
    flushDataCache();
    return p;
  }



  static void* loadImg(const char* filename, int* w, int* h){
    FRESULT res;
    FIL fil;
    res = f_open(&fil, filename, FA_READ);
    if (res == FR_OK) {
      unsigned int br;
      f_lseek(&fil, 0);
      unsigned char magic[2];
      f_read(&fil, magic, 2, &br);
      if(magic[0] == 'B' || magic[1] == 'M') {
        f_lseek(&fil, 0);
        void* p = decodeBMP(&fil, w, h);
        f_close(&fil);
        return p;
      }else if(magic[0] == 0xFF || magic[1] == 0xD8) {
        //printf("jpg\n");
        f_lseek(&fil, 0);
        void* p = decodeJPG(&fil, w, h);
        f_close(&fil);
        return p;
      }else{
        f_close(&fil);
        return 0;
      }
    }
    return 0;
  }


  static JSValue js_image_get_buffer(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Image *image = (Image*)JS_GetOpaque(this_val);
    int byte_length = image->width * image->height * 2;
    return JS_NewArrayBuffer(ctx, (uint8_t*)image->buffer, byte_length, 0, 0, 0);
  }
  static JSValue js_image_get_width(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Image *image = (Image*)JS_GetOpaque(this_val);
    return JS_NewInt32(ctx, image->width);
  }
  static JSValue js_image_get_height(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Image *image = (Image*)JS_GetOpaque(this_val);
    return JS_NewInt32(ctx, image->height);
  }

  static JSValue js_util_loadImg(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    JSValue ret = JS_UNDEFINED;
    if(argc==1){
      const char *str = JS_ToCString(ctx, argv[0]);
      if (str) {
        int width;
        int height;
        void* buffer = loadImg(str, &width, &height);
        if(buffer){
          // int byte_length = width * height * 2;
          // JSValue buffer = JS_NewArrayBuffer(ctx, (uint8_t*)p, byte_length, 0, 0, 0);
          // ret = JS_NewObject(ctx);
          // JS_SetPropertyStr(ctx, ret, "buffer", buffer);
          // JS_SetPropertyStr(ctx, ret, "width", JS_NewInt32(ctx, width));
          // JS_SetPropertyStr(ctx, ret, "height", JS_NewInt32(ctx, height));

          Image* p = new Image();
          p->buffer = buffer;
          p->width = width;
          p->height = height;

          JSValue obj = JS_NewObjectClass(ctx, js_image_class_id);
          JS_SetOpaque(obj, p);
          p->js_obj = obj;
          ret = obj;

        }
        JS_FreeCString(ctx, str);
      }
    }
    return ret;
  }

  void util_init() {

    JSValue util = JS_NewObject(ctx);
    JS_SetPropertyStr(ctx, util, "getRGB565", JS_NewCFunction(ctx, js_util_getRGB565, "getRGB565", 0));
    JS_SetPropertyStr(ctx, util, "loadImg", JS_NewCFunction(ctx, js_util_loadImg, "loadImg", 0));
    JS_SetPropertyStr(ctx, global, "util", util);

    

    JS_NewClassID(&js_image_class_id);
    JSClassDef image_def = {
      .class_name = "Image",
      .finalizer = js_image_finalizer,
    };
    JS_NewClass(rt, js_image_class_id, &image_def);

    JSValue image_proto = JS_NewObject(ctx);

    JS_SetClassProto(ctx, js_image_class_id, image_proto);

    JS_DefinePropertyGetSet(ctx, image_proto, JS_NewAtom(ctx, "buffer"), JS_NewCFunction(ctx, js_image_get_buffer, "buffer", 0), JS_UNDEFINED, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE);
    JS_DefinePropertyGetSet(ctx, image_proto, JS_NewAtom(ctx, "width"), JS_NewCFunction(ctx, js_image_get_width, "width", 0), JS_UNDEFINED, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE);
    JS_DefinePropertyGetSet(ctx, image_proto, JS_NewAtom(ctx, "height"), JS_NewCFunction(ctx, js_image_get_height, "height", 0), JS_UNDEFINED, JS_PROP_CONFIGURABLE | JS_PROP_ENUMERABLE);

    // JS_SetPropertyStr(ctx, image_proto, "read", JS_NewCFunction(ctx, js_image_read, "read", 1));
    // JS_SetPropertyStr(ctx, image_proto, "write", JS_NewCFunction(ctx, js_image_write, "write", 2));
    // JS_SetPropertyStr(ctx, image_proto, "seek", JS_NewCFunction(ctx, js_image_seek, "seek", 1));


  }
`;

module.exports = { util };